#include "connector.h"
#include "event_loop.h"
#include "channel.h"
#include <assert.h>
// #include <QDebug>

el::Connector::Connector(EventLoop* loop, SocketAddrIn& addr)
	: loop_(loop), addr_(addr), channel_(NULL), state_(DISCONNECT), retry_ms_(init_retry_ms_), connect_(false), sock_(Socket(-1))
{
#if WINCE
	max_retry_ms_ = 1000;
#else
	max_retry_ms_ = 10000;
#endif
}

el::Connector::~Connector()
{
}

void el::Connector::start()
{
// 	qDebug()<<"connector_start:"<<GetTickCount();
	connect_ = true;
	loop_->runInLoop(Functor(this, &Connector::startInLoop));
}

void el::Connector::restart()
{
	assert(loop_->isInLoopThread());
	state_ = DISCONNECT;
	retry_ms_ = init_retry_ms_;
	connect_ = true;
	startInLoop();
}

void el::Connector::stop()
{
	connect_ = false;
	loop_->runInLoop(Functor(this, &Connector::stopInLoop));
}

void el::Connector::handleWrite()
{
	if (state_ == CONNECTING)
	{
		FD fd = removeChannel();
		int err = sock_.getSocketError();
		if (err || sock_.isSelfConnect())
		{
			sock_.closeFD();
			retry(fd);
		}
		else
		{
			state_ = CONNECTED;
			if (connect_) new_connect_cb_(fd);
			else sock_.closeFD();
		}
	}
	else
	{
		assert(state_ == DISCONNECT);
	}
}

void el::Connector::handleError()
{
// 	qDebug()<<"connector_handleError:"<<GetTickCount();
	if (state_ == CONNECTING)
	{
		state_ = DISCONNECT;
		int sockfd = removeChannel();
		retry(sockfd);
	}
}

void el::Connector::startInLoop()
{
	assert(loop_->isInLoopThread());
	assert(state_ == DISCONNECT);

	if (connect_) connect();
// 	qDebug()<<"startInLoop:"<<GetTickCount();
}

void el::Connector::stopInLoop()
{
	assert(loop_->isInLoopThread());
	if (state_ == CONNECTING)
	{
		state_ = DISCONNECT;
		int sockfd = removeChannel();
		retry(sockfd);
	}
}

void el::Connector::connect()
{
	sock_ = Socket(Socket::getTcpSocket());
	FD fd = sock_.fd();
	if (fd < 0)
	{
		connect_error_cb_(WSAGetLastError());
		return;
	}
	sock_.nonBlockSocket(true);
	sock_.reuserAddr(true);

	int rtn = sock_.connect(addr_);
	rtn = rtn == 0 ? 0 : WSAGetLastError();
	switch (rtn)
	{
	case 0:
	case WSAEINPROGRESS:
	case WSAEINTR:
	case WSAEISCONN:
	case WSAEWOULDBLOCK:
		connecting(fd);
		break;
	case WSAEADDRINUSE:
	case WSAECONNREFUSED:
	case WSAEADDRNOTAVAIL:
	case WSAENETUNREACH:
		sock_.closeFD();
		retry(fd);
		break;
	default:
		connect_error_cb_(WSAGetLastError());
		sock_.closeFD();
		break;
	}
}

void el::Connector::connecting(FD fd)
{
	state_ = CONNECTING;
	assert(channel_ == NULL);
	channel_ = new Channel(loop_, fd);
	channel_->setWriteCallBack(Functor(this, &Connector::handleWrite));
	channel_->setExceptionCallBack(Functor(this, &Connector::handleError));
	channel_->enableWriteEvent();
	channel_->enableExecptionEvent();
}

void el::Connector::retry(FD fd)
{
	state_ = DISCONNECT;
	if (connect_)
	{
		loop_->runAfter(retry_ms_, Functor(this, &Connector::startInLoop));
		retry_ms_ = retry_ms_ * 2 > max_retry_ms_ ? max_retry_ms_ : retry_ms_ *2;
	}
}

el::FD el::Connector::removeChannel()
{
	channel_->disableAllEvent();
	channel_->remove();
	FD fd = channel_->fd();
	loop_->queueInLoop(Functor(this, &Connector::releaseChannel));
	return fd;
}

void el::Connector::releaseChannel()
{
	channel_->release();
	channel_ = NULL;
}


